﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MosCraft.Utilities
{
    public class ChunkCoordinate : IEquatable<ChunkCoordinate>
    {
        /// <summary>
        /// The X coordinate
        /// </summary>
        public int X;

        /// <summary>
        /// The Z coordinate
        /// </summary>
        public int Z;

        /// <summary>
        /// Initializes a new instance of the <see cref="ChunkCoordinate"/> class.
        /// </summary>
        public ChunkCoordinate()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ChunkCoordinate"/> class.
        /// </summary>
        /// <param name="x">The x.</param>
        /// <param name="z">The z.</param>
        /// <remarks></remarks>
        public ChunkCoordinate(int x, int z)
        {
            X = x;
            Z = z;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ChunkCoordinate"/> class.
        /// </summary>
        /// <param name="other">Base of another <see cref="ChunkCoordinate"/> class </param>
        public ChunkCoordinate(ChunkCoordinate other)
        {
            X = other.X;
            Z = other.Z;
        }

        /// <summary>
        /// Clones this instance.
        /// </summary>
        /// <returns><see cref="ChunkCoordinate"/> class</returns>
        public ChunkCoordinate Clone()
        {
            return (ChunkCoordinate)this.MemberwiseClone();
        }

        /// <summary>
        /// Adds two coordinates together.
        /// </summary>
        /// <param name="a">A.</param>
        /// <param name="b">The b.</param>
        /// <returns>The result of the operator.</returns>
        /// <remarks></remarks>
        public static ChunkCoordinate operator +(ChunkCoordinate a, ChunkCoordinate b)
        {
            return new ChunkCoordinate(a.X + b.X, a.Z + b.Z);
        }

        /// <summary>
        /// Subtracts one coordinate from another.
        /// </summary>
        /// <param name="a">A.</param>
        /// <param name="b">The b.</param>
        /// <returns>The result of the operator.</returns>
        /// <remarks></remarks>
        public static ChunkCoordinate operator -(ChunkCoordinate a, ChunkCoordinate b)
        {
            return new ChunkCoordinate(a.X - b.X, a.Z - b.Z);
        }

        /// <summary>
        /// Multiplies two coordinates together.
        /// </summary>
        /// <param name="a">A.</param>
        /// <param name="b">The b.</param>
        /// <returns>The result of the operator.</returns>
        /// <remarks></remarks>
        public static ChunkCoordinate operator *(ChunkCoordinate a, ChunkCoordinate b)
        {
            return new ChunkCoordinate(a.X * b.X, a.Z * b.Z);
        }

        /// <summary>
        /// Divides one coordinate from another.
        /// </summary>
        /// <param name="a">A.</param>
        /// <param name="b">The b.</param>
        /// <returns>The result of the operator.</returns>
        /// <remarks></remarks>
        public static ChunkCoordinate operator /(ChunkCoordinate a, ChunkCoordinate b)
        {
            return new ChunkCoordinate(a.X / b.X, a.Z / b.Z);
        }

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.</returns>
        /// <remarks></remarks>
        public bool Equals(ChunkCoordinate other)
        {
            if (other == null)
                return false;
            return other.X == this.X && other.Z == this.Z;
        }

        /// <summary>
        /// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns><c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.</returns>
        /// <remarks></remarks>
        public override bool Equals(object obj)
        {
            return Equals((ChunkCoordinate)obj);
        }

        /// <summary>
        /// Returns a hash code for this instance.
        /// </summary>
        /// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.</returns>
        /// <remarks></remarks>
        public override int GetHashCode()
        {
            return this.X.GetHashCode() ^ this.Z.GetHashCode();
        }

        /// <summary>
        /// Gets the distance between this and another ChunkCoordinate.
        /// </summary>
        /// <param name="other">The ChunkCoordinate to compare to.</param>
        /// <returns></returns>
        /// <remarks></remarks>
        public double DistanceTo(ChunkCoordinate other)
        {
            return Math.Sqrt(Math.Pow(other.Z - Z, 2) + Math.Sqrt(Math.Pow(other.X - X, 2)));
        }

        /// <summary>
        /// Gets the Floored distance between this and another ChunkCoordinate.
        /// </summary>
        /// <param name="other">The ChunkCoordinate to compare to.</param>
        /// <returns></returns>
        /// <remarks></remarks>
        public int DistanceToFloored(ChunkCoordinate other)
        {
            return (int) Math.Floor(this.DistanceTo(other));
        }

        /// <summary>
        /// Floors this instance.
        /// </summary>
        /// <returns></returns>
        /// <remarks></remarks>
        public ChunkCoordinate Floor()
        {
            return new ChunkCoordinate((int)X, (int)Z);
        }

        /// <summary>
        /// The toString function for ChunkCoordinate.
        /// </summary>
        /// <returns>Returns a formatted vector string (X,Z)</returns>
        public override string ToString()
        {
            return "<" + this.X + ", " + this.Z + ">";
        }
    }
}
